package safe;

import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.concurrent.TimeUnit;

/**
 * @author lzm
 * @date 2025/2/20 20:02
 * @description 多线程版的累加器 - 使用CAS解决线程不靠谱问题
 */
public class AdderV6 {

    private static final Unsafe unsafe;

    private static final long valueOffset;

    static {
        try {
            // 通过反射获取 Unsafe 实例
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            unsafe = (Unsafe) theUnsafe.get(null);

            // 获取 i 字段的内存偏移量
            Field valueField = AdderV6.class.getDeclaredField("i");
            valueOffset = unsafe.objectFieldOffset(valueField);
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }

    int i = 0;

    /**
     * 多线程版的累加器
     */
    public void add() throws InterruptedException {
        // 为每个线程创建计算任务
        Runnable task = () -> {
            // 将变量i加1，重复这个操作10000次
            for (int j = 0; j < 10000; j++) {
                // 使用CAS操作的+1
                int expectedValue;
                int newValue;
                do {
                    expectedValue = i;
                    newValue = expectedValue + 1;
                } while (!compareAndSet(expectedValue, newValue));
            }
        };
        // 一共有5个线程，每个线程都对变量i进行一万次+1的操作
        for (int j = 0; j < 5; j++) {
            new Thread(task, "线程-" + j).start();
        }
        TimeUnit.SECONDS.sleep(1);
    }

    /**
     * 模拟CAS实现
     * @param expectedValue 预期值，也就是每个线程修改前读到的变量值
     * @param newValue      每个线程修改后的新值
     * @return 比较结果
     * 注意事项：
     *      1. 该方法必须要使用synchronized关键字修饰，否则仍然会出现线程不靠谱问题
     *      2. 真正的CAS操作一般是使用unsafe类实现的，我这里只是模拟了一下
     */
    private synchronized boolean compareAndSet(int expectedValue, int newValue) {
        return unsafe.compareAndSwapInt(this, valueOffset, expectedValue, newValue);
    }


    public static void main(String[] args) throws InterruptedException {
        AdderV6 adder = new AdderV6();
        adder.add();
        System.out.println("计算结果是：" + adder.i);
    }

}
